// 日志器模块
// 1. 抽象日志器基类
// 2. 派生子类：同步/异步日志器类
// 同步日志器：将日志直接通过落地模块句柄进行日志落地
// 异步日志器: 将数据首先写入到缓冲区中，通过单的的线程/进程进行日志落地

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

namespace gjclog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;

        Logger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end())
        {
        }

        // 获取日志器名称
        const std::string &name()
        {
            return _logger_name;
        }
        // 完成构造日志消息对象并进行格式化，得到格式化后的日志消息字符串
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，进行日志的格式化，然后落地

            // 1. 判断当前的日志是否达到了输出等级
            if (LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            // 2. 对fmt格式化字符串和不定参进行字符串组织得到日志消息字符串
            va_list ap;
            va_start(ap, fmt); // 确定不定参的起始地址
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap); // 实现不定参的格式化输出功能
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap); // 结束，将ap指针置空
            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，进行日志的格式化，然后落地

            // 1. 判断当前的日志是否达到了输出等级
            if (LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            // 2. 对fmt格式化字符串和不定参进行字符串组织得到日志消息字符串
            va_list ap;
            va_start(ap, fmt); // 确定不定参的起始地址
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap); // 实现不定参的格式化输出功能
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap); // 结束，将ap指针置空
            serialize(LogLevel::value::INFO, file, line, res);
            free(res);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，进行日志的格式化，然后落地

            // 1. 判断当前的日志是否达到了输出等级
            if (LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            // 2. 对fmt格式化字符串和不定参进行字符串组织得到日志消息字符串
            va_list ap;
            va_start(ap, fmt); // 确定不定参的起始地址
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap); // 实现不定参的格式化输出功能
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap); // 结束，将ap指针置空
            serialize(LogLevel::value::WARN, file, line, res);
            free(res);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，进行日志的格式化，然后落地

            // 1. 判断当前的日志是否达到了输出等级
            if (LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            // 2. 对fmt格式化字符串和不定参进行字符串组织得到日志消息字符串
            va_list ap;
            va_start(ap, fmt); // 确定不定参的起始地址
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap); // 实现不定参的格式化输出功能
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap); // 结束，将ap指针置空
            serialize(LogLevel::value::ERROR, file, line, res);
            free(res);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 构造日志消息对象，进行日志的格式化，然后落地

            // 1. 判断当前的日志是否达到了输出等级
            if (LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            // 2. 对fmt格式化字符串和不定参进行字符串组织得到日志消息字符串
            va_list ap;
            va_start(ap, fmt); // 确定不定参的起始地址
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap); // 实现不定参的格式化输出功能
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap); // 结束，将ap指针置空
            serialize(LogLevel::value::FATAL, file, line, res);
            free(res);
        }

    protected:
        // 对构造日志消息对象中重复的代码进行封装
        void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
        {
            // 3. 构造LogMsg对象
            LogMsg msg(LogLevel::value::DEBUG, line, file, _logger_name, str);
            // 4. 通过格式化工具对LogMsg进行格式化，得到格式化日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5. 进行日志落地
            log(ss.str().c_str(), ss.str().size());
        }

    protected:
        // 抽象接口完成实际的落地输出：同步 / 异步
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;                         // 互斥锁，保证线程安全
        std::string _logger_name;                  // 日志器名称
        std::atomic<LogLevel::value> _limit_level; // 日志器限制等级：原子
        Formatter::ptr _formatter;                 // 格式化对象
        std::vector<LogSink::ptr> _sinks;          // 落地方向
    };

    // 同步日志器类
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        // 实际落地函数：将缓冲区中的数据落地
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
            {
                return;
            }
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    // 异步日志器类
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks, AsyncType lopper_type)
            : Logger(logger_name, level, formatter, sinks), _lopper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), lopper_type))
        {
        }
        // 数据写入函数：将数据写入缓冲区
        void log(const char *data, size_t len)
        {
            _lopper->push(data, len);
        }
        // 实际落地函数：将缓冲区中的数据落地
        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
            {
                return;
            }
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSzie());
            }
        }

    private:
        AsyncLooper::ptr _lopper;
    };

    // 日志器类型
    enum class LoggerType
    {
        LOGGER_SYNC, // 同步日志器
        LOGGER_ASYNC // 异步日志器
    };
    // 建造者模式建造日志器，简化用户使用复杂度
    // 1. 抽象日志器建造者类
    //    1.1 设置日志器类型
    //    1.2 将不同类型日志器的创建放在同一个日志器建造者类中
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC), _limit_level(LogLevel::value::DEBUG), _looper_type(AsyncType::ASYNC_SAFE) {}

        void buildLoggerType(LoggerType type) { _logger_type = type; }
        void bulidLoggerName(const std::string &name) { _logger_name = name; }
        void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
        void buildFormatter(const std::string &pattern) { _formatter = std::make_shared<Formatter>(pattern); }
        // 异步工作器非安全模式工作模式接口
        void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
        // 提供类型，创建日志器，最后添加到vector中
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        // 建造日志器：局部 & 全局
        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;           // 异步工作器工作模式：安全 / 非安全
        LoggerType _logger_type;          // 日志器类型
        std::string _logger_name;         // 日志器名称
        LogLevel::value _limit_level;     // 日志器限制等级
        Formatter::ptr _formatter;        // 格式化对象
        std::vector<LogSink::ptr> _sinks; // 落地方向
    };

    // 2. 派生具体的建造者类：局部日志器建造者 & 全局日志器建造者
    // 局部日志器建造者
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        // 局部日志器建造接口
        Logger::ptr build() override
        {
            // 1. 必须要有日志器名
            assert(_logger_name.empty() == false);
            // 2. 没有日志器时，构造默认日志器
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 3. 没给出落地方向时，默认落地方向
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }

            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                // 异步日志器
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            // 同步日志器
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    // 日志器管理器：单例模式-懒汉模式
    class LoggerManager
    {
    public:
        // 单例对象获取接口
        static LoggerManager &getInstance()
        {
            // 线程安全：C++11针对静态局部变量在编译层面实现了线程安全，静态局部变量没有构造完成之前，其他线程进入回阻塞
            static LoggerManager eton;
            return eton;
        }
        // 日志器添加接口：将指定的日志器添加到管理数组中
        void addLogger(Logger::ptr &logger)
        {
            if (hasLogger(logger->name()))
            {
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        // 日志器判断接口：判断是否存在指定的日志器
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        // 日志器获取接口：通过名称获取日志器
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr(); // 没找到返回空智能指针
            }
            return it->second;
        }
        // 日志器获取接口：默认日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        // 单例模式：构造函数私有化
        LoggerManager()
        {
            std::unique_ptr<gjclog::LoggerBuilder> builder(new gjclog::LocalLoggerBuilder());
            builder->bulidLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }

    private:
        std::mutex _mutex;                                     // 互斥锁
        Logger::ptr _root_logger;                              // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers; // 日志器管理数组
    };

    // 全局日志器建造者
    // 在局部日志器基础上，增加自动将日志器添加到单例对象中的功能
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        // 全局日志器建造并添加管理接口
        Logger::ptr build() override
        {
            // 1. 必须要有日志器名
            assert(_logger_name.empty() == false);
            // 2. 没有日志器时，构造默认日志器
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 3. 没给出落地方向时，默认落地方向
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                // 异步日志器
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }else{
                // 同步日志器
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}

#endif